﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EvoAlgLib
{
    public class GPCrossover: Crossover
    {
        
        private double p_cross;

        public GPCrossover(double p_cross)
        {
            this.p_cross = p_cross;
        }

        public List<Individual> cross(List<Individual> parents)
        {
            List<Individual> offsprings = new List<Individual>();

            //GPIndividual father = new GPIndividual((GPIndividual)parents[0]);
            //GPIndividual mother = new GPIndividual((GPIndividual)parents[1]);

            GPIndividual father = new GPIndividual((GPIndividual)parents[0]);
            GPIndividual mother = new GPIndividual((GPIndividual)parents[1]);

            offsprings.Add(father);
            offsprings.Add(mother);

            if (LibStatics.getRandomizer().NextDouble() > this.p_cross)
            {
                return offsprings;
            }
            

            Node fnode = (Node)father.getGenotype();
            Node mnode = (Node)mother.getGenotype();

            Node frnode = fnode.getRandomSubTree();
            Node mrnode = mnode.getRandomSubTree();

            Node frp = frnode.getParent();
            Node mrp = mrnode.getParent();

            if (frp == null)
            {
                father.setGenotype(mrnode);
            } else
            {
                for (int i = 0; i < frp.getChildren().Count(); i++)
                {
                    if (frp.getChildren()[i] == frnode)
                    {
                        frp.setChild(mrnode, i);
                        break;
                    }
                }
            }
            mrnode.setParent(frp);

            if (mrp == null)
            {
                mother.setGenotype(frnode);
            } else
            {
                for (int i = 0; i < mrp.getChildren().Count(); i++)
                {
                    if (mrp.getChildren()[i] == mrnode)
                    {
                        mrp.setChild(frnode, i);
                        break;
                    }
                }
            }
            frnode.setParent(mrp);

            propagateDepth(frnode);
            propagateDepth(mrnode);

            father.dirty();
            mother.dirty();

            return offsprings;
        }

        private static void copyNode(Node n1, Node n2)
        {
            n1._func = n2._func;
            n1.setChildren(n2.getChildren());
            n1.setDepth(n2.getDepth());
        }

        private static void propagateDepth(Node n)
        {
            Node tmp = n;
            while (tmp.getParent() != null)
            {
                Node p = tmp.getParent();
                p.setDepth(Math.Max(p.getDepth(), tmp.getDepth() + 1));
                tmp = tmp.getParent();
            }
        }
    }
}
